﻿//************************************************************************
//* Written by Jirat Boomuang
//* Filename: ApplicationMain.cs
//* Revision: 1.1
//* Author: JiratB
//* Platform: .NET Framework 4.0
//* Description: Main part of the application. The main business logic
//*             is under Run()
//************************************************************************

//************************************************************************
//* Revision History
//------------------------------------------------------------------------
//* Revision  Date      Signature    Description
//------------------------------------------------------------------------
//* 1.0       20131027  JiratB       - First release
//* 1.1       20131030  JiratB       - Fixed bugs and tested with CRM 2013
//************************************************************************

//-- For Standard Components ---------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;

using System.ServiceModel;
using System.ServiceModel.Description;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

using Microsoft.Crm.Sdk.Messages;
//------------------------------------------------------------------------

//-- Solution References -------------------------------------------------
using AdaCRMUtilities;
//------------------------------------------------------------------------

namespace CrmSdkConsoleApplication
{
    public class ApplicationMain
    {
        #region Constants

        protected const string CONNECTING_PLEASEWAIT = "\n\n...Connecting to your CRM Organization, please wait...\n";
        protected const string SUCCESSFUL_CONNECTION = "\n...Your session is successfully connected to your CRM Organization...\n";

        #endregion

        #region Data Members

        protected int loop;

        protected OrganizationServiceProxy serviceProxy;

        protected Int32 accountRunningNumber;
        protected Int32 contactRunningNumber;

        #endregion

        #region Utilities

        public void ValidateArgs(string[] args)
        {
            if (args.Length < 1)
            {
                loop = 100;
            }
            else
            {
                loop = Convert.ToInt32(args[0]);
            }
        }//-- End Of ValidateArgs() --

        public void InitializeAccountRunningNumber(OrganizationServiceProxy sp)
        {
            try
            {
                accountRunningNumber = 0;

                string xmlRetrieveMax = @"
                    <fetch distinct='true' mapping='logical' aggregate='false' count='1'>
                        <entity name='account'>
                            <attribute name='jiratb_runningnumber' alias='runningnumber_max'/>
                            <order attribute='jiratb_runningnumber' descending='true' />
                        </entity>
                    </fetch>
                    ";

                EntityCollection runningnumberMaxResult = sp.RetrieveMultiple(
                        new FetchExpression(xmlRetrieveMax)
                    );

                foreach (var c in runningnumberMaxResult.Entities)
                {
                    accountRunningNumber = (Int32)((AliasedValue)c["runningnumber_max"]).Value + 1;                   
                }//-- End Of foreach --
            }
            catch (Exception e)
            {
                //-- Catch the exception here but continue --
                Console.WriteLine("\n***************************************");
                Console.WriteLine("[ERROR]: {0}", e.Message);
                Console.WriteLine("***************************************");

                accountRunningNumber = 1;
            }

            Console.WriteLine("\n***************************************");
            Console.Write("** Next Account's running number = {0}", accountRunningNumber);
            Console.WriteLine("\n***************************************");
        }//-- End of InitializeAccountRunningNumber() --

        public void InitializeContactRunningNumber(OrganizationServiceProxy sp)
        {
            try
            {
                contactRunningNumber = 0;

                string xmlRetrieveMax = @"
                    <fetch distinct='true' mapping='logical' aggregate='false' count='1'>
                        <entity name='contact'>
                            <attribute name='jiratb_runningnumber' alias='runningnumber_max'/>
                            <order attribute='jiratb_runningnumber' descending='true' />
                        </entity>
                    </fetch>
                    ";

                EntityCollection runningnumberMaxResult = sp.RetrieveMultiple(
                        new FetchExpression(xmlRetrieveMax)
                    );

                foreach (var c in runningnumberMaxResult.Entities)
                {
                    contactRunningNumber = (Int32)((AliasedValue)c["runningnumber_max"]).Value + 1;                  
                }//-- End Of foreach --
            }
            catch (Exception e)
            {
                //-- Catch the exception here but continue --
                Console.WriteLine("\n***************************************");
                Console.WriteLine("[ERROR]: {0}", e.Message);
                Console.WriteLine("***************************************");

                accountRunningNumber = 1;
            }

            Console.WriteLine("\n***************************************");
            Console.Write("** Next Contact's running number = {0}", contactRunningNumber);
            Console.WriteLine("\n***************************************");
        }//-- End of InitializeContactRunningNumber() --

        public void CreateAnInteger(
            OrganizationServiceProxy sp,
            string entitySchemaName, 
            string schemaName,
            string displayNameEN,
            string displayNameTH)
        {
            IntegerAttributeMetadata attribute = new IntegerAttributeMetadata();

            attribute.SchemaName = schemaName;

            //-- Create display name in Thai and English ------------------------------------
            Label displayNameLabel = new Label();
            LocalizedLabel englishLabel = new LocalizedLabel(
                    displayNameEN,
                    1033
                );

            LocalizedLabel thaiLabel = new LocalizedLabel(
                    displayNameTH,
                    1054
                );

            displayNameLabel.LocalizedLabels.Add(englishLabel);
            displayNameLabel.LocalizedLabels.Add(thaiLabel);

            attribute.DisplayName = displayNameLabel;
            //---------------------------------------------------------------------------------

            attribute.RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None);

            attribute.Description = new Label("Generated by Jirat's code - mainly for performance testing purpose", 1033);

            attribute.Format = IntegerFormat.None;

            attribute.MaxValue = 999999999; //-- 1 billion minus 1 --

            attribute.MinValue = 0;

            //-- Create the request instance --
            CreateAttributeRequest createAttributeRequest = new CreateAttributeRequest
            {
                EntityName = entitySchemaName,
                Attribute = attribute
            };

            //-- Call SDK service to create the attribute --
            sp.Execute(createAttributeRequest);
        }//-- End of CreateAnInteger() --

        #endregion

        #region Create attributes

        protected void CreateAttributes(OrganizationServiceProxy sp)
        {
            //-- Create "Running Number" for "Account" --
            try
            {
                AdaEntityUtils.CreateAnInteger(
                        sp,
                        "account",
                        "jiratb_runningnumber",
                        "None",
                        "Running Number",
                        "หมายเลขลำดับ",
                        "Generated by JiratB's code",
                        "999999999",
                        "0"                       
                    );
            }
            catch (Exception e)
            {
                //-- Catch the exception here but continue --
                Console.WriteLine("\n***************************************");
                Console.WriteLine("[ERROR]: {0}", e.Message);
                Console.WriteLine("***************************************");
            }

            //-- Create "Running Number" for "Contact" --
            try
            {
                AdaEntityUtils.CreateAnInteger(
                        sp,
                        "contact",
                        "jiratb_runningnumber",
                        "None",
                        "Running Number",
                        "หมายเลขลำดับ",
                        "Generated by JiratB's code",
                        "999999999",
                        "0"                        
                    );
            }
            catch (Exception e)
            {
                //-- Catch the exception here but continue --
                Console.WriteLine("\n***************************************");
                Console.WriteLine("[ERROR]: {0}", e.Message);
                Console.WriteLine("***************************************");
            }
        }//-- End Of CreateAttributes() --

        #endregion

        #region Run()
        public void Run(ServerConnection.Configuration serverConfig, string[] args)
        {
            try
            {
                //-- Validate args --
                ValidateArgs(args);

                Console.WriteLine(CONNECTING_PLEASEWAIT);
                using (serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig))
                {
                    Console.WriteLine(SUCCESSFUL_CONNECTION);

                    //-- Attribute creation --
                    CreateAttributes(serviceProxy);

                    //-- Initialize Running Numbers here --
                    char key;
                    do
                    {
                        InitializeAccountRunningNumber(serviceProxy);

                        Console.Write("Satisfied? ");
                        key = Console.ReadKey().KeyChar;
                        Console.WriteLine();

                    } while ((key != 'y') && (key != 'Y'));

                    do
                    {
                        InitializeContactRunningNumber(serviceProxy);

                        Console.Write("Satisfied? ");
                        key = Console.ReadKey().KeyChar;
                        Console.WriteLine();

                    } while ((key != 'y') && (key != 'Y'));
                    //-------------------------------------

                    //-- Account Generation starts here --
                    string accountName;
                    string accountNumber;
                    int numberOfEmployees;
                    double revenue;
                    double creditLimit;

                    DateTime startTime = DateTime.Now;

                    int i = loop;
                    while (i-- > 0)
                    {
                        #region Create primary contact

                        Guid primaryContactId = Guid.Empty;

                        try
                        {
                            Entity contact = new Entity("contact");

                            string contactFirstname = AdaRandomUtils.RandomUpperCase(10);
                            string contactLastname = AdaRandomUtils.RandomUpperCase(20);
                            string contactBusinessPhone = AdaRandomUtils.RandomPhoneNumber("+66", "81", 7);
                            string contactEmail = 
                                String.Format(
                                AdaRandomUtils.RandomUpperCase(10) + 
                                "@" + AdaRandomUtils.RandomUpperCase(10) +
                                '.' + AdaRandomUtils.RandomUpperCase(3)
                                ).ToLower();

                            contact.Attributes["firstname"] = contactFirstname;
                            contact.Attributes["lastname"] = contactLastname;
                            contact.Attributes["telephone1"] = contactBusinessPhone;
                            contact.Attributes["emailaddress1"] = contactEmail;
                            contact.Attributes["jiratb_runningnumber"] = contactRunningNumber++;

                            Guid contactGUID = serviceProxy.Create(contact);
                            primaryContactId = contactGUID;

                            //*
                            Console.WriteLine("\n[{0}]: Contact \"{1}\" is created. FirstName = {2}, " +
                                "LastName = {3}, BusinessPhone = {4}, Email = {5}, RunningNumber = {6}",
                                i, contactGUID.ToString(), contactFirstname, contactLastname,
                                contactBusinessPhone, contactEmail, contactRunningNumber);
                            //*/

                        }
                        catch (Exception e)
                        {

                            Console.WriteLine(
                                String.Format("***** Cannot create the primary 'contact'; ERROR: [{0}] ******", e.Message)
                                );

                        }//-- End of catch --
                        #endregion

                        #region Create Account

                        Entity account = new Entity("account");

                        accountName = AdaRandomUtils.RandomUpperCase(10) + " " + AdaRandomUtils.RandomUpperCase(10);
                        accountNumber = AdaRandomUtils.RandomUpperCase(4) + AdaRandomUtils.RandomNumber(7);
                        numberOfEmployees = AdaRandomUtils.RandomInt(5, 20000);
                        revenue = numberOfEmployees * (double)AdaRandomUtils.RandomInt(300000, 9999999);
                        creditLimit = revenue / AdaRandomUtils.RandomInt(5, 24);

                        account.Attributes["accountnumber"] = accountNumber;
                        account.Attributes["name"] = accountName;
                        account.Attributes["emailaddress1"] =
                            String.Format(
                                AdaRandomUtils.RandomUpperCase(10) +
                                "@" + AdaRandomUtils.RandomUpperCase(10) +
                                '.' + AdaRandomUtils.RandomUpperCase(3)
                                ).ToLower();
                        account.Attributes["telephone1"] = AdaRandomUtils.RandomPhoneNumber("+66", "2", 7);
                        account.Attributes["telephone2"] = AdaRandomUtils.RandomPhoneNumber("+66", "2", 7);
                        account.Attributes["fax"] = AdaRandomUtils.RandomPhoneNumber("+66", "2", 7);
                        account.Attributes["websiteurl"] = "htt://www.doesnotexist.com";
                        account.Attributes["creditlimit"] = (double)creditLimit;
                        account.Attributes["revenue"] = (double)revenue;
                        account.Attributes["numberofemployees"] = numberOfEmployees;
                        account.Attributes["jiratb_runningnumber"] = accountRunningNumber++;

                        #region Assign Primary Contact
                        try
                        {
                            if (primaryContactId != Guid.Empty)
                            {
                                account["primarycontactid"] = new EntityReference("contact", primaryContactId);
                            }
                        }
                        catch (Exception e)
                        {
                        }
                        #endregion

                        Guid accountGUID = serviceProxy.Create(account);

                        #region Display The Created Account
                        //*
                        Console.WriteLine("\n[{0}]: Account \"{1}\" is created. AccountNumber = {2}, " +
                            "CreditLimit = {3}, Revenue = {4}, NumberOfEmployees = {5}",
                            i, accountName, accountNumber, creditLimit, revenue, numberOfEmployees);
                        //*/
                        #endregion

                        #endregion

                        #region Associate 'Account' to 'Contact' as the 'Parent Customer'
                        try
                        {
                            if ((accountGUID != Guid.Empty) && (primaryContactId != Guid.Empty))
                            {
                                AssociateRequest areq = new AssociateRequest();

                                areq.Target = new EntityReference("account", accountGUID);

                                areq.RelatedEntities = new EntityReferenceCollection();
                                areq.RelatedEntities.Add(new EntityReference("contact", primaryContactId));

                                areq.Relationship = new Relationship("contact_customer_accounts");

                                serviceProxy.Execute(areq);

                                ///*
                                Console.WriteLine("\n[{0}]: Relationship between Contact \"{1}\" and Account \"{2}\" is created.",
                                i, primaryContactId.ToString(), accountGUID.ToString());
                                Console.WriteLine(
                                    "====================================================================="
                                );
                                 //*/
 
                            }
                        }
                        catch (Exception e)
                        {
                            
                            Console.WriteLine(
                                String.Format("***** Cannot create relationship between 'contact' and 'account'; ERROR: [{0}] ******", e.Message)
                                );
                              
                        }
                        #endregion

                    }//-- end of while loop --

                    DateTime endTime = DateTime.Now;

                    Console.WriteLine("\n***************************************");
                    Console.WriteLine("Start Time: {0}", startTime);
                    Console.WriteLine("End Time: {0}", endTime);
                    Console.WriteLine("Time Spent: {0} (hours:minutes:seconds.milliseconds)", endTime - startTime);
                    Console.WriteLine("TOTAL: {0} loops, {1} records, {2} associations", loop, loop*2, loop);
                    Console.WriteLine("Total Time Spent: {0} milliseconds", (endTime - startTime).TotalMilliseconds);
                    Console.WriteLine("Time Spent Per Loop: {0} milliseconds", (endTime - startTime).TotalMilliseconds/loop);
                    Console.WriteLine("Time Spent Per Operation: {0} milliseconds", ((endTime - startTime).TotalMilliseconds/3) / loop);
                    Console.WriteLine("Estimated loops per second = {0} loops", Math.Floor(1000/ ((endTime - startTime).TotalMilliseconds / loop) ));
                    Console.WriteLine("Estimated loos per minute = {0} loops", Math.Floor( (60 * 1000) / ((endTime - startTime).TotalMilliseconds / loop)) );
                    Console.Write("Estimated loops per hour = {0} loops", Math.Floor( (60 * 60 * 1000) / ((endTime - startTime).TotalMilliseconds / loop)) );
                    Console.WriteLine("\n***************************************");

                    Console.WriteLine("Press any key to continue");
                    Console.ReadKey();
                    //-- Account Generation ends here --

                }//-- End of using() --

            }//-- End of try --
            catch (Exception e)
            {
                Console.WriteLine("\n***************************************");
                Console.Write("** [ERROR]: {0}", e.Message);
                Console.WriteLine("\n***************************************");
            }//-- End of catch() --

        }//-- End of Run() --
        #endregion

    }//-- End Of Class ApplicationMain --
}//-- End Of Namespace --

//************************************************************************
// End of file
